package com.example.forum.component;

import com.alibaba.fastjson.JSON;
import com.example.forum.common.AppResult;
import com.example.forum.common.ResultCode;
import com.example.forum.exception.ApplicationException;
import com.example.forum.model.ChatMessage;
import com.example.forum.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
@Slf4j
@ServerEndpoint(value = "/chat/{username}",encoders = ServerEncoder.class) //  传用户名为参数,指定编码器
public class WebSocketServer {
    private Session session;//  保存与每一个客户端的回话<不是HttpSession>
    private String username;//  用于识别 客户端的Session

    //  定义一个全局map，保存每一个客户端的Session
    private static final Map<String,Session> sessionMap = new ConcurrentHashMap<>();

    /**
     * socket连接成功执行该方法
     * @param session 此次回话
     * @param username 用户名
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("username") String username) {
        //  给对象赋初值
        this.session = session;
        this.username = username;

        //  把 Session存入 map
        if(!sessionMap.containsKey(username)) {
            sessionMap.put(username,session);
        }
        log.info(username + " 已上线.");
    }



    /**
     * 关闭连接执行方法
     * @param username 用户名
     */
    @OnClose
    public void onClose(@PathParam("username") String username) {
        if(sessionMap.containsKey(username)) {
            sessionMap.remove(username);
        }
        log.info(username + " 已下线.");
    }

    /**
     * 服务器收到客户端信息时调用方法
     * @param message 客户端发送的信息
     */
    @OnMessage
    public void onMessage(String message) {
        //  发送消息时，先约定信息格式
        //  1. 把 字符串信息 转换成 java对象
        ChatMessage msg = JSON.parseObject(message,ChatMessage.class);

        //  2. 处理信息内容
        //  2.1 判断用户是否存在
        if(StringUtil.isEmpty(msg.getToUser())) {
            log.warn(ResultCode.FAILED_USER_NOT_EXISTS.toString());
            throw new ApplicationException(ResultCode.FAILED_USER_NOT_EXISTS.getMessage());
        }
        //  2.2 发送信息
        Session toSession = sessionMap.get(msg.getToUser());
        sendMessage(AppResult.success(msg),toSession);

    }

    /**
     * 连接出错时调用方法
     * @param error 错误
     */
    @OnError
    public void onError(Throwable error) {
        log.warn("连接出错");
        error.printStackTrace();
    }

    /**
     * 给指定客户端发送信息
     * @param appResult 封装后的信息
     * @param toSession 指定回话
     */
    private void sendMessage(AppResult appResult, Session toSession) {
        if(toSession == null) {
            return;
        }
        try {
            toSession.getBasicRemote().sendObject(appResult);
        } catch (IOException | EncodeException e) {
            e.printStackTrace();
        }
    }

}
